﻿#include "FbxLoader.h"
#include "fbxsdk.h"
#include "Graphics.h"

namespace DYResources
{
	void ReadMat(FbxMesh* pMesh, int pPolygonIdx, Vertex& pVertex);

	// 加载mesh
	MeshPtr FbxLoader::LoadMeshes()
	{
		MeshData* meshData = ReadMeshData();
		if (meshData == nullptr) return nullptr;
		MeshPtr mesh = std::make_shared<Mesh>();
		mesh->SetMeshData(meshData);
		return mesh;
	}

	MeshData* FbxLoader::ReadMeshData()
	{
		if (mFbxMeshes.empty())
		{
			printf("fbx hasn't mesh");
			return nullptr;
		}

		FbxMesh* fbxMesh = mFbxMeshes[0];
		if (fbxMesh->GetControlPointsCount() == 0)
		{
			printf("mesh has no vertex");
			return nullptr;
		}

		int numTriangles = fbxMesh->GetPolygonCount();	// 三角面数量
		int numVertices = numTriangles * 3;
		Vertex* vertices = new Vertex[numVertices];
		unsigned int* indices = new unsigned int[numVertices];
		int vertexCnt = numVertices;
		int indiceCnt = numVertices;

		// 处理顶点数据
		FbxVector4* lCtrPoints = fbxMesh->GetControlPoints();
		FbxLayerElementNormal* lLayerNormal = fbxMesh->GetElementNormal(0);	// 第0层的法线
		FbxLayerElementUV* lLayerUV = fbxMesh->GetElementUV(0);	// 第0层的UV

		int vertexIdx = 0;
		for (int polygonIdx = 0; polygonIdx < numTriangles; polygonIdx++)
		{
			// 只处理三角形
			int lPolygonSize = fbxMesh->GetPolygonSize(polygonIdx);
			if (lPolygonSize != 3)
			{
				delete[] vertices;
				delete[] indices;
				printf("only process triangle, polygon size is %d \n", lPolygonSize);
				return nullptr;
			}

			for (int i = 0; i < lPolygonSize; i++)
			{
				Vertex lVertex;

				// Control Point
				int controlPointIndex = fbxMesh->GetPolygonVertex(polygonIdx, i);
				FbxVector4 lCtrPoint = lCtrPoints[controlPointIndex];

				// 读取位置
				lVertex.position[0] = lCtrPoint.mData[0];
				lVertex.position[1] = lCtrPoint.mData[1];
				lVertex.position[2] = lCtrPoint.mData[2];

				// 读取法线
				int lNormalIdx = 0;
				switch (lLayerNormal->GetMappingMode())
				{
				case FbxLayerElement::EMappingMode::eByControlPoint:
					lNormalIdx = controlPointIndex;
					break;
				case FbxLayerElement::EMappingMode::eByPolygonVertex:
					lNormalIdx = vertexIdx;
					break;
				default:
					printf("not support normal mapping mode: %d \n", (int)lLayerNormal->GetMappingMode());
					break;
				}

				FbxVector4 lNormal;
				int directIdx = 0;
				switch (lLayerNormal->GetReferenceMode())
				{
				case FbxLayerElement::EReferenceMode::eDirect:
					lNormal = lLayerNormal->GetDirectArray().GetAt(lNormalIdx);
					break;
				case FbxLayerElement::EReferenceMode::eIndex:
				case FbxLayerElement::EReferenceMode::eIndexToDirect:
					directIdx = lLayerNormal->GetIndexArray().GetAt(lNormalIdx);
					lNormal = lLayerNormal->GetDirectArray().GetAt(directIdx);
					break;
				default:
					break;
				}

				lVertex.normal[0] = lNormal.mData[0];
				lVertex.normal[1] = lNormal.mData[1];
				lVertex.normal[2] = lNormal.mData[2];

				// 读取UV
				int lUVIdx = 0;
				switch (lLayerUV->GetMappingMode())
				{
				case FbxLayerElement::EMappingMode::eByControlPoint:
					lUVIdx = controlPointIndex;
					break;
				case FbxLayerElement::EMappingMode::eByPolygonVertex:
					lUVIdx = vertexIdx;
					break;
				default:
					printf("not support uv mapping mode: %d \n", (int)lLayerUV->GetMappingMode());
					break;
				}

				FbxVector4 lUV;
				switch (lLayerUV->GetReferenceMode())
				{
				case FbxLayerElement::EReferenceMode::eDirect:
					lUV = lLayerUV->GetDirectArray().GetAt(lUVIdx);
					break;
				case FbxLayerElement::EReferenceMode::eIndex:
				case FbxLayerElement::EReferenceMode::eIndexToDirect:
					directIdx = lLayerUV->GetIndexArray().GetAt(lUVIdx);
					lUV = lLayerUV->GetDirectArray().GetAt(directIdx);
					break;
				default:
					break;
				}

				lVertex.texcoord[0] = lUV.mData[0];
				lVertex.texcoord[1] = 1 - lUV.mData[1];

				ReadMat(fbxMesh, polygonIdx, lVertex);

				indices[vertexIdx] = vertexIdx;
				vertices[vertexIdx] = lVertex;
				vertexIdx++;
			}
		}

		MeshData* meshData = new MeshData();
		meshData->name = fbxMesh->GetName();
		meshData->vertices = vertices;
		meshData->indices = indices;
		meshData->vertexCnt = vertexCnt;
		meshData->indiceCnt = indiceCnt;
		return meshData;
	}

	void ReadMat(FbxMesh* pMesh, int pPolygonIdx, Vertex& pVertex)
	{
		FbxLayerElementMaterial* leMat = pMesh->GetElementMaterial();
		int matIdx = leMat->GetIndexArray().GetAt(pPolygonIdx);

		int index = 0;
		FbxSurfaceMaterial* surfaceMat = nullptr;
		switch (leMat->GetReferenceMode())
		{
		case FbxLayerElement::EReferenceMode::eDirect:
			surfaceMat = leMat->mDirectArray->GetAt(matIdx);
			break;
		case FbxLayerElement::EReferenceMode::eIndexToDirect:
			index = leMat->GetIndexArray().GetAt(matIdx);
			surfaceMat = leMat->mDirectArray->GetAt(index);
			break;
		default:
			return;
		}
	}
}